package com.codelab.statemachine.task;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.NumberUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.codelab.statemachine.mapstruct.TaskEntityMapper;
import com.codelab.statemachine.mybatis.dataobject.TaskDO;
import com.codelab.statemachine.mybatis.mapper.TaskMapper;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.statemachine.StateMachine;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

@Service
public class CommonTaskService extends ServiceImpl<TaskMapper,TaskDO> implements TaskService, TaskMachineService {

    @Autowired
    private TaskStateMachineBuilder stateMachineBuilder;

    @Autowired
    private BeanFactory beanFactory;

    @Override
    public TaskDO getTaskById(String id) {
        return getById(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String insertTask(Task task,String pId) {
        //TaskDO taskDO = new TaskDO();
        //BeanUtil.copyProperties(task,taskDO);
        TaskDO taskDO = TaskEntityMapper.INSTANCE.task2Entity(task);
        taskDO.setPId(pId);
        save(taskDO);

        if(CollUtil.isNotEmpty(task.getChildsTask())) {
            task.getChildsTask().forEach(t->insertTask(t,taskDO.getId()));
        }
        return taskDO.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void insertBatchTask(List<TaskDO> tasks) {
        saveBatch(tasks);
    }

    @Override
    public void updateProcess(String taskid, double progress) {
        update(new UpdateWrapper<TaskDO>().
                lambda().
                set(TaskDO::getProgress,progress).
                set(TaskDO::getUpdateTime, LocalDateTime.now()).
                eq(TaskDO::getId,taskid));
    }

    @Override
    public void updateMessage(String taskid, String msg) {
        update(new UpdateWrapper<TaskDO>().
                lambda().
                set(TaskDO::getMessage,msg).
                set(TaskDO::getUpdateTime, LocalDateTime.now()).
                eq(TaskDO::getId,taskid));
    }

    @Override
    public void updateStatus(String taskid, String status) {

        update(new UpdateWrapper<TaskDO>().
                lambda().
                set(TaskDO::getStatus,status).
                set(TaskDO::getUpdateTime, LocalDateTime.now()).
                eq(TaskDO::getId,taskid));

    }

    @Override
    public ProgressPOJO getChildTasksProgress(String taskid) {

        List<TaskDO> childs = getChildTasks(taskid);
        return getChildTasksProgress(childs);

    }

    @Override
    public List<TaskDO> getChildTasks(String pTaskid) {
        return list(new QueryWrapper<TaskDO>().eq("pid",pTaskid));
    }

    @Override
    public ProgressPOJO getChildTasksProgress(List<TaskDO> childs) {
        boolean isDone = true;
        double rate = 0;
        String code = "0";

        if(CollUtil.isEmpty(childs)){
            return new ProgressPOJO().setProgress(rate).setDone(isDone).setCode(code);
        }
        for (TaskDO task : childs) {
            rate += NumberUtil.div(task.getProgress(),childs.size());
            if(!NumberUtil.equals(task.getProgress(),1)){
                isDone = false;
            }
            if(task.getStatus().equals(TaskStates.FAIL.toString())){
                code="-1";
            }
        }

        ProgressPOJO pojo = new ProgressPOJO().setProgress(rate).setDone(isDone).setCode(code);
        return pojo;
    }

    @Override
    public TaskDO getRootTaskById(String taskid) {
        TaskDO curTask = getTaskById(taskid);

        if(curTask == null) {
            return null;
        }

        if(curTask.getPId() == null) {
            return curTask;
        }
        return getRootTaskById(curTask.getPId());
    }

    @Override
    public List<TaskDO> getAllTaskById(String taskid) {
        TaskDO root = getRootTaskById(taskid);
        List<TaskDO> tasks = new ArrayList<>();
        if(root == null){
            return tasks;
        }
        Queue<TaskDO> queue = new LinkedList<>();
        queue.offer(root);
        while(!queue.isEmpty()){
            TaskDO taskDO = queue.poll();
            queue.addAll(getChildTasks(taskDO.getId()));
            tasks.add(taskDO);
        }
        return tasks;
    }

    @Override
    public List<TaskDO> listAllTask() {
        return super.list();
    }

    @Override
    public String createTaskMachine(Task task) {

        String taskId = insertTask(task,null);
        startTaskMachine(taskId);
        System.out.println("执行createTaskMachine");
        return taskId;
    }

    @Override
    public void startTaskMachine(String taskId) {
        StateMachine<TaskStates, TaskEvents> stateMachine = null;
        try {
            stateMachine = stateMachineBuilder.build(beanFactory);
            stateMachine.start();
            stateMachine.getExtendedState().getVariables().put("taskid",taskId);
            stateMachine.sendEvent(TaskEvents.PREPARE);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("状态机创建失败");
        }

    }
}
